package org.bsc.mgwt.client.widget;

import java.util.List;
import java.util.logging.Logger;

import org.bsc.mgwt.client.MobileResourceAware;
import org.bsc.mgwt.client.res.EditableListCss;
import org.bsc.mgwt.client.widget.celllist.CellEditHandler;
import org.bsc.mgwt.client.widget.celllist.EditableCell;
import org.bsc.mgwt.client.widget.celllist.HasCellEditHandler;
import org.bsc.mgwt.client.widget.event.CellEditEvent;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.touch.client.Point;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.SimpleEventBus;
import com.googlecode.mgwt.ui.client.widget.Button;
import com.googlecode.mgwt.ui.client.widget.CellList;
import com.googlecode.mgwt.ui.client.widget.celllist.Cell;
import com.googlecode.mgwt.ui.client.widget.celllist.CellSelectedEvent;
import com.googlecode.mgwt.ui.client.widget.celllist.CellSelectedHandler;

/**
 * 
 * @author softphone
 *
 * @param <T>
 */
public class EditableCellList<T> extends CellList<T> implements HasCellEditHandler, MobileResourceAware{

	final Logger logger = Logger.getLogger(EditableCellList.class.getName());
		
	private boolean editing = false;
	private int editingIndex = -1;
	private java.util.List<T> renderedList = null;
	
	private final EventBus eventBus = new SimpleEventBus();
	
	final Button deleteButton = new Button("Delete") {{
		setImportant(true);
		addStyleName(mobileBundleIPhone.editableCellList().delete());
		setSmall(true);
		/*
		addTapHandler( new TapHandler() {
			
			@Override
			public void onTap(TapEvent event) {
				logger.info( "delete");
				try {
					onDeleteItem( editingIndex );
				}
				finally {				
					cancelEditing( getRowElement(editingIndex) );
				}
			}
		});
		*/
	}};

	/**
	 * 
	 * @author softphone
	 *
	 */
	class EditableCellSelectedHandlerProxy implements CellSelectedHandler {
		CellSelectedHandler delegate;

		public EditableCellSelectedHandlerProxy(CellSelectedHandler delegate) {
			this.delegate = delegate;
		}

		@Override
		public void onCellSelected(CellSelectedEvent event) {			
			if( !isEditing() ) delegate.onCellSelected(event);
		}
		
	}
	
	/**
	 * 
	 * @author softphone
	 *
	 */
	class EditableCellSelectedHandlerAdapter implements CellSelectedHandler {
		
		
		@Override
		public void onCellSelected(CellSelectedEvent event) {
			
			final int index = event.getIndex();
			
			logger.info( "select " + event);
			if( !isEditing() || !isCellEditable(index)) {
				return;
			}
			
			final Element selectedRow = getRowElement(event.getIndex());
			final EditableCell.Style style = getCellStyle( index );
			
			if( style == EditableCell.Style.EDIT) {
				
				if( editingIndex !=  index ) {
					
					if( editingIndex != -1 ) {
						cancelEditing( getRowElement(editingIndex) );
					}
					
					selectedRow.appendChild( deleteButton.getElement());
					editingIndex = index;
	
				}
				else {
					
					Point pt = event.getTouchPoint();
					
					Element e = deleteButton.getElement();
					
					logger.fine( "pt " + pt + "\n" + 
								"e.getAbsoluteTop="		+ e.getAbsoluteTop() 	+ "\n" + 
								"e.getAbsoluteBottom="	+ e.getAbsoluteBottom() + "\n" + 
								"e.getAbsoluteLeft="	+ e.getAbsoluteLeft() 	+ "\n" + 
								"e.getAbsoluteRight="	+ e.getAbsoluteRight() 	+ "\n" 
								);
					
					try { 
	
						//
						// PATCH DUE THE EVENT DON'T HAS PROPAGATED TO BUTTON
						// TODO INVESTIGATE WHY NOT
						//
						if( pt.getY() >= e.getAbsoluteTop() && 
							pt.getX() <= e.getAbsoluteRight() && 
							pt.getY() <= e.getAbsoluteBottom() && 
							pt.getX() >= e.getAbsoluteLeft() ) {
	
							onCellSelectedToDelete(event);
								
						}
						
					}
					finally {
						cancelEditing( selectedRow );
											
					}
							
				}
			}
			else if( style == EditableCell.Style.ADD ) {

				if( editingIndex != -1  && editingIndex != index ) {					
					cancelEditing( getRowElement(editingIndex) );
				}
				editingIndex = -1;

				onCellSelectedToAdd(event);
				
			}
			else {
				// DO NOTHING
			}
		}
		
	}
	
	public EditableCellList(Cell<T> cell) {
		this(cell, mobileBundleIPhone.editableCellList() );
	}

	public EditableCellList(Cell<T> cell, EditableListCss css) {
		super(cell, css);
		
		
	}

	private HandlerRegistration hr;
	
	@Override
	protected void onAttach() {
		super.onAttach();
		
		hr = super.addCellSelectedHandler( new EditableCellSelectedHandlerAdapter());
		
	}

	@Override
	protected void onDetach() {
		super.onDetach();
		
		hr.removeHandler();
		
	}

	public boolean addModel( T model ) {
		return this.addModel( model, false );
		
	}
	public boolean addModel( T model, boolean render ) {
		if( renderedList == null ) return false;
		if( model == null ) return false;

		boolean result =  renderedList.add(model);
		
		if( result && render ) render();
		
		return result;
		
	}
	
	public boolean removeModel( T model ) {
		return this.removeModel(model, false);
	}
	
	public boolean removeModel( T model, boolean render ) {
		if( renderedList == null ) return false;
		if( model == null ) return false;

		boolean result =  renderedList.remove(model);
		
		if( result && render ) render();
		
		return result;
		
	}
	
	public T getModel( int index ) {
	
		if( renderedList == null ) return null;

		return renderedList.get(index);
	}
	
	/**
	 * 
	 * @param index
	 * @return
	 */
	protected Element getRowElement( int index ) {
		Widget w = getWidget();
		
		Element e = w.getElement();

		Node n = e.getChild(index);
		
		if( n instanceof Element ) {
			
			return (Element) n;
		}
		
		return null;
		
	}

	/**
	 * 
	 * @param row
	 */
	protected void cancelEditing( Element row ) {

		if( editingIndex != -1 ) {
			try {
				row.removeChild(deleteButton.getElement());
			} catch( Exception e ) {
				logger.warning( "error on cancel editing row " + editingIndex);
			}
		}
		
		editingIndex = -1;
	}
	
	/**
	 * 
	 * @param index
	 * @return
	 */
	private boolean isCellEditable( int index ) {
		if( cell instanceof EditableCell ) {
			return ((EditableCell<T>)cell).isEditable(index, getModel(index));
		}
		
		return true;
	}

	/**
	 * 
	 * @param index
	 * @return
	 */
	private EditableCell.Style getCellStyle( int index ) {
		if( cell instanceof EditableCell ) {
			return ((EditableCell<T>)cell).getStyle(index, getModel(index));
		}
		
		return EditableCell.Style.EDIT;
	}
	
	/**
	 * 
	 * @param on
	 */
	protected void renderEditing( boolean on ) {
		
		EditableListCss editCss = (EditableListCss)css;
		Widget w = getWidget();
		
		Element e = w.getElement();
		
		for( int i=0; i < e.getChildCount(); ++i ) {
			
			Node n = e.getChild(i);
			
			logger.fine( "node " + n.getNodeName() + " class " + editCss.edit());

			if( n instanceof Element ) {
				
				Element ee = (Element) n;
				
				final boolean editable = isCellEditable(i);
				final EditableCell.Style style = getCellStyle(i);
				
				if( on ) {
				
					if( style == EditableCell.Style.EDIT && editable) {
						ee.addClassName(editCss.edit());
					}
					else if( style == EditableCell.Style.ADD ) {
						ee.addClassName(editCss.add());						
					}
					else {
						// TOD define class for NONE
					}
				}
				else {
					if( style == EditableCell.Style.EDIT && editable) {
						ee.removeClassName(editCss.edit());
						if( editingIndex == i ) cancelEditing( ee );
					}
					else if( style == EditableCell.Style.ADD ) {
						ee.removeClassName(editCss.add());
					}
					else {
						// TOD remove class for NONE
					}

				}
			}
		}
		
	}
	
	/**
	 * 
	 */
	public void cancelEditing( ) {
		
		if( editingIndex != -1 ) {
			cancelEditing( getRowElement(editingIndex) );
		}

	}
	
	/**
	 * toggle edit flag and render if required
	 * 
	 * @param render if true data will be rendered
	 */
	public void toggleEdit( boolean render ) {
		if( render ) renderEditing( !editing );
				
		editing = !editing;
	}
	
	/**
	 *  
	 *  toggle edit flag and render
	 *  
	 */
	public void toggleEdit() {
		toggleEdit(true);
		
	}

	@Override
	public void render(List<T> models) {
		if( renderedList!=null ) renderedList.clear();
		
		renderedList = new java.util.ArrayList<T>(models);
		
		super.render(models);
		
		if( isEditing() ) renderEditing( true );
	}

	/**
	 * repaint the current data
	 */
	protected void render() {	
		if( renderedList== null ) return;
		
		super.render(renderedList);
		
		if( isEditing() ) renderEditing( true );
	}

	public final boolean isEditing() {
		return editing;
	}

	protected final void setEditing(boolean editing) {
		this.editing = editing;
	}

	@Override
	public HandlerRegistration addCellSelectedHandler(CellSelectedHandler cellSelectedHandler) {
		return super.addCellSelectedHandler( new EditableCellSelectedHandlerProxy(cellSelectedHandler));
	}

	///
	/// HANDLER
	///
	
	@Override
	public com.google.web.bindery.event.shared.HandlerRegistration addCellEditHandler(CellEditHandler handler) {		
		return CellEditEvent.register(eventBus, handler);
	}

	private void onCellSelectedToDelete(CellSelectedEvent event) {
		eventBus.fireEvent( new CellEditEvent( CellEditEvent.Action.DELETE, event ));
	}

	private void onCellSelectedToAdd(CellSelectedEvent event) {
		eventBus.fireEvent( new CellEditEvent( CellEditEvent.Action.ADD, event ));
	}
	
}
